#include <string.h>
#include "tinyFS.h"
#include "libDisk.c"

int diskCount = 0, currentDisk = -1, openFiles = 0;

typedef struct OpenFile {
   char *filename; //8 chars + null
   fileDescriptor id;
   int disk;
   int sizeInBytes;
   int inodeBlockNumber;
   int offset;
   struct OpenFile *next;
} OpenFile;

OpenFile *rootFile = NULL, *currFile = NULL;

/* this function allows the user to see a map of all blocks with the non-free
blocks clearly designated. You can return this as a linked list or a bit map
which you can use to display the map with */
void tfs_displayFragments() {
   char buffer[256] = "";
   int i, j;
   if(currentDisk > -1)
      for(i = 0; i < 40; i++) {
         readBlock(currentDisk, i, buffer);
         for(j = 0; j < 256; j += 2)
            printf("%x %x\n", buffer[j], buffer[j+1]);
      }
}

int getFreeBlock(int inode, int *oldRootInode){
   char val;
   char buffer[256] = "";
   readBlock(currentDisk, 0, buffer);
   val = buffer[3];
   if(inode) {
      *oldRootInode = buffer[2];
      buffer[2] = val;
   }
   buffer[3] = (char)(((int)val) + 1);
   writeBlock(currentDisk, 0, buffer);
   return (int)val;
}

/* Makes a blank TinyFS file system of size nBytes on the file specified by
‘filename’. This function should use the emulated disk library to open the
specified file, and upon success, format the file to be mountable. */
int tfs_mkfs(char *filename, int nBytes) {
   int diskNo = openDisk(filename, nBytes), blocks, i, j;
   char buffer[256] = "";
   blocks = nBytes / BLOCKSIZE;
   //Set up superBlock type, magic,root inode, freeblocklist
   memmove(buffer, "\x01\x45\x00\x01", 4);
   writeBlock(diskNo, 0, buffer);
   memset(buffer, 0, BLOCKSIZE);
   //Set up free blocks type, magic, pointer, blank
   for(i = 1; i < blocks; i++) {
      strcpy(buffer, "\x04\x45");
      if(i + 1 <= nBytes / BLOCKSIZE)
         buffer[2] = i + 1;
      writeBlock(diskNo, i, buffer);
      memset(buffer, 0, BLOCKSIZE);
   }
   diskCount++;
   return 0;
}

/* tfs_mount(char *filename) “mounts” a TinyFS file system located within
‘filename’. tfs_unmount(void) “unmounts” the currently mounted file system.
As part of the mount operation, tfs_mount should verify the file system is the
correct type. Only one file system may be mounted at a time. Use tfs_unmount to
cleanly unmount the currently mounted file system. Must return a specified
success/error code. */
int tfs_mount(char *filename) {
   int mav = verify(filename), success = -1;
   printf("%d\n", mav);
   if(mav > -1) {
      currentDisk = mav;
      success = 0;
   }
   return success;
}

int tfs_unmount(void) {
   int success = -1;
   currentDisk = -1;
   if(currentDisk == -1)
      success = 0;
   return success;
}

/* Opens a file for reading and writing on the currently mounted file system.
Creates a dynamic resource table entry for the file, and returns a file
descriptor (integer) that can be used to reference this file while the
filesystem is mounted. */
fileDescriptor tfs_openFile(char *name){
   fileDescriptor f = 0;
   int oldRootInode;
   char buffer[256] = "";

   if(strlen(name) <= 8 && currentDisk > -1) {
      // Open file list
      OpenFile *new = malloc(sizeof(OpenFile));
      new->filename = malloc(strlen(name));
      strcpy(new->filename, name);
      new->disk = currentDisk;
      new->id = openFiles++;
      new->sizeInBytes = 0;
      f = new->id;
      new->offset = 0;
      // Inode type, magic, filename, block accosiated with file
      strcpy(buffer, "\x02\x45");
      new->inodeBlockNumber = getFreeBlock(1, &oldRootInode);
      if(currFile) {
         currFile->next = new;
         currFile = new;
         currFile->next = NULL;
      } else {
         rootFile = new;
         currFile = rootFile;
         new->next = NULL;
      }

      buffer[2] = oldRootInode;
      strcat(buffer + 4, name);
      writeBlock(currentDisk, new->inodeBlockNumber, buffer);
   }

   return f;
}
/* Closes the file, de-allocates all system/disk resources, and removes
table entry */
int tfs_closeFile(fileDescriptor FD) {
   return 0;
}

/* Writes buffer ‘buffer’ of size ‘size’, which represents an entire file’s
content, to the file system. Sets the file pointer to 0 (the start of file)
when done. Returns success/error codes. */
int tfs_writeFile(fileDescriptor FD,char *buffer, int size) {
   //check if file exists on current disk
   //determin if buffer will fit on disk
   //check inode for block to use, if there is not one get a new block, if
   //there is one, fill it up more. may need to store byte offset in the inode.
   //it would make it easy to use the inode as a file extent block as well. just
   // make sure the offset into the inode is correct
   return 0;
}

/* deletes a file and marks its blocks as free on disk. */
int tfs_deleteFile(fileDescriptor FD) {
   return 0;
}

/* reads one byte from the file and copies it to buffer, using the current
file pointer location and incrementing it by one upon success. If the file
pointer is already at the end of the file then tfs_readByte() should return an
error and not increment the file pointer. */
int tfs_readByte(fileDescriptor FD, char *buffer) {
   return 0;
}

OpenFile *findFile(fileDescriptor FD) {
   OpenFile *tmp = NULL;
   if(rootFile) {
      tmp = rootFile;
      while(tmp) {
         printf("%d == %d\n", tmp->id, FD );
         if(tmp->id == FD)
            break;
         tmp = tmp->next;
      }
   }
   return tmp;
}
/* change the file pointer location to offset (absolute).
Returns success/error codes.*/
int tfs_seek(fileDescriptor FD, int offset) {
   int success = -1;
   OpenFile *toChange = findFile(FD);
   if(toChange && offset > 0 && toChange->sizeInBytes > offset) {
      toChange->offset = offset;
      success = 0;
   }
   return success;
}
